﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Json;
using System.Threading;
using System.Windows.Media;
using System.Xml.Serialization;

namespace Foundation
{
    public static class Serializer
    {
        public const string JsonExtension = ".json";
        public static readonly Type[] KnownTypes = new[] { typeof(Dictionary<string, string>), typeof(SolidColorBrush), typeof(MatrixTransform) };
        private static readonly Dictionary<string, KeyValuePair<object, Type>> Items = new Dictionary<string, KeyValuePair<object, Type>>();
        private static readonly EventWaitHandle Event = new ManualResetEvent(true);
       
        static Serializer()
        {
            new Thread(SerializeThreadBody).Start();
        }

        private static void SerializeThreadBody()
        {
            do
            {
                if (Items.Count == 0)
                    Event.Reset();
                Event.WaitOne();
                var pair = Items.First();
                lock (Items) Items.Remove(pair.Key);
                var file = pair.Key;
                var item = pair.Value.Key;
                var type = pair.Value.Value;
                item.SerializeDataContract(type, file);
            } while (Event != null);
        }

        public static void SerializeDataContractAsync<TItem>(this TItem item, string file = null)
        {
            file = file ?? item.GetFileNameForSerialization(JsonExtension);
            lock (Items)
            {
                if (!Items.ContainsKey(file))
                    Items.Add(file, new KeyValuePair<object, Type>(item, typeof (TItem)));   
            }

            Event.Set();
        }

        public static void SerializeDataContract(this object item, Type type, string file = null)
        {
            if (string.IsNullOrEmpty(file))
                file = item.GetFileNameForSerialization(JsonExtension);
            var serializer = new DataContractJsonSerializer(type, KnownTypes);
            using (var stream = File.Create(file))
                serializer.WriteObject(stream, item);
        }

        public static void SerializeDataContract<TItem>(this TItem item, string file = null)
        {
            if (string.IsNullOrEmpty(file))
                file = item.GetFileNameForSerialization(JsonExtension);
            var serializer = new DataContractJsonSerializer(typeof (TItem), KnownTypes);
            using (var stream = File.Create(file))
                serializer.WriteObject(stream, item);
        }

        public static TItem DeserializeDataContract<TItem>(string file = null)
        {
            if (string.IsNullOrEmpty(file))
                file = FileNameProvider.GetFileNameForSerialization<TItem>(JsonExtension);
            var serializer = new DataContractJsonSerializer(typeof (TItem), KnownTypes);
            using (var stream = File.OpenRead(file))
                return (TItem) serializer.ReadObject(stream);
        }

        public static void Serialize<TItem>(this TItem item, string file = null)
        {
            if (string.IsNullOrEmpty(file))
                file = item.GetFileNameForSerialization();
            var serializer = new XmlSerializer(typeof (TItem));
            using (var writer = new StreamWriter(file))
            {
                serializer.Serialize(writer, item);
            }
        }

        public static TItem Deserialize<TItem>(string file = null)
        {
            if (string.IsNullOrEmpty(file))
                file = FileNameProvider.GetFileNameForSerialization<TItem>();
            var serializer = new XmlSerializer(typeof (TItem));
            if (!File.Exists(file)) return default(TItem);
            using (var reader = new StreamReader(file))
            {
                var item = (TItem) serializer.Deserialize(reader);
                return item;
            }
        }

        public static bool TrySerialize<TItem>(this TItem item, string file = null)
        {
            try
            {
                item.Serialize(file);
            }
            catch
            {
                return false;
            }

            return true;
        }

        public static TItem TryDeserialize<TItem>(string file = null)
        {
            try
            {
                return Deserialize<TItem>(file);
            }
            catch
            {
                return default(TItem);
            }
        }

        public static bool TrySerializeDataContract<TItem>(this TItem item, string file = null)
        {
            try
            {
                item.SerializeDataContract(file);
            }
            catch
            {
                return false;
            }

            return true;
        }

        public static TItem TryDeserializeDataContract<TItem>(string file = null)
        {
            try
            {
                return DeserializeDataContract<TItem>(file);
            }
            catch
            {
                return default(TItem);
            }
        }
    }
}
